﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Diagnostics;

namespace AbyssCombatLogger
{
    public class LogParser
    {

        public long fileLength;
        public long charCount = 0;

        //private DateTime dtStamp;
        //private DateTime dtFirstStamp;
        //private bool firstStamp = true; //dont forget to reset after loggin is disabled

        bool bCancelParse = false;

        public bool CancelParse
        {
            get { return bCancelParse; }
            set { bCancelParse = value; }
        }
        private float simulationFrequency = 1.0f;

        public float SimulationFrequency
        {
            get { return simulationFrequency; }
            set { simulationFrequency = value; }
        }

        //public delegate void DisplayUpdateDelegate();
        //public DisplayUpdateDelegate mDisplayUpdate = null;
     

        /// <summary>
        ///  Note: do EventCalculations init. first
        /// </summary>
        /// <param name="parseStatus"></param>
        /// <param name="mostRecentMessage"></param>
        /// <param name="calc"></param>
        /// <param name="cancelMonitor"></param>
        public LogParser(ref ProgressBar parseStatus, ref TextBox mostRecentMessage, ref EventHandlers calc, ref bool cancelMonitor)
        {
            this.pbParseStatus = parseStatus;
            this.tbMostRecentMessage = mostRecentMessage;
            this.ParseAccountant = calc;
            this.bCancelParse = cancelMonitor;
            //firstStamp = true;
        }

        ProgressBar pbParseStatus = null;
        TextBox tbMostRecentMessage = null;
        EventHandlers ParseAccountant = null;
        private string currentTarget;

        public string CurrentTarget
        {
            get { return currentTarget; }
        }

        //public delegate void SimpleMessageDelegate(string message);
        //SimpleMessageDelegate simpleMessageHandler = null;

        private void SimpleMessage(string simpleRecentMessage)
        {
            //if (simpleMessageHandler !=null)
            //simpleMessageHandler.Invoke(simpleRecentMessage);
            ChangeText(tbMostRecentMessage, simpleRecentMessage); //for now this message will not be logged it is a friendly message.
        }

        private delegate void ChangeTextDelegate(Control ctrl, string text);

        public static void ChangeText(Control ctrl, string str)
        {
            if (!ctrl.IsDisposed)
            {
                if (ctrl.InvokeRequired)
                {
                    ctrl.Invoke(new ChangeTextDelegate(ChangeText), new object[] { ctrl, str });
                }
                else
                {
                    if (ctrl.Text != str)
                    {
                        ctrl.Text = str;
                        ctrl.Update();
                    }
                }
            }

        } //end of reference (about 10 lines)
        const string your = "Your "; //your <spell> .. noun
        //ex: <actor> = <actor> | <actor>'s
        const string heals = " heals ";//ex: <actor> <spell> affects <target>    or   <actor>'s <spell> affects <target>
        const string hits = " hits "; //ex: <actor> hits <target>
        const string crits = " crits "; //ex: <actor> crits <target>
        const string sFor = " for ";  //ex: <actor> <verb> <target> for <damage>... keep the extra space off because we need to find the extra space not skip past it to get to the numerical damage
        //const string criticallyDoesSomething = " critically ";
        const string criticallySlashes = " critically slashes "; //ex: <actor> <verb> <target>
        const string criticallyCrushes = " critically crushes ";//ex: <actor> <verb> <target>
        const string criticallyPierces = " critically pierces "; //ex: <actor> <verb> <target>
        const string criticallyHeals = " critically heals ";//ex: <actor> <verb> <target>
        const string successfullyCast = " successfully cast ";//ex: YOU <verb> <target>, only happens for local user (you) 
        const string startsCasting = " starts casting ";
        const string casts = " casts ";
        string dies = " dies ";

        /// <summary>
        ///  The newLine argument to parse can be multi-line by seperating them with delimeters
        /// </summary>
        /// <param name="newLine"></param>
        /// <returns> true if successful</returns>
        protected string curLine = null;
        int lioVerb = -1; //made class wide variable
        string targetFullName = "";
        int lioHit = -1;
        int lioCrit = -1;
        int lioHeal = -1;
        int lioSuccessfullyCast = -1;
        int lioStartsCasting = -1;
        int lioCriticallyHeals = -1;
        int lioCriticallySlashes = -1;
        int lioCriticallyCrushes = -1;
        int lioCriticallyPierces = -1;
        DateTime timeStamp;

        int ioHit = -1, ioCrit = -1, ioHeals = -1,
    ioSucessfullyCast = -1, ioCriticallyPierces = -1, ioStartsCasting = -1,
    ioCriticallySlashes = -1, ioCriticallyCrushes = -1, ioCriticallyHeals = -1;
        int ioFor = -1, ioEndOfFor = -1;
        int ioQuoteInActorName = -1, ioDelimAfterAmount = -1;
        int ioEndOfFirstWord = -1;

        string spellCast = "";

        int ioDies = -1;
        bool actorIsPet = false; 
        bool actorNameHasSpace = false;
        int ioCasts = -1;
        int lioCasts = -1;

        bool actorNameHasYour = false;
        bool hasVerb = false;
        int ioVerb = -1;
        string actorFullName = null;
        int ioStartOfFirstWordOrPlayer = -1;// = curLine.IndexOf("] ") + "] ".Length;

        int ioYour = -1;

        private int DelayPerLine_ForSimpleSimulation = 0;

        public int DelayPerLine_ForSimulation_InMilliSeconds
        {
            get { return DelayPerLine_ForSimpleSimulation; }
            set { DelayPerLine_ForSimpleSimulation = value; }
        }
        bool bLineByLineSimulation = false;

        public bool RealTimeSimulation
        {
            get { return bLineByLineSimulation; }
            set { bLineByLineSimulation = value; }
        }
        
        System.Timers.Timer updateTime = null;

        public bool ParseAndSaveNewData(string newLine)
        {
            if (newLine == null || newLine == "")
                return true;

            string[] delimiter = { "\r\n" };
            string[] lstLines = newLine.Split(delimiter, StringSplitOptions.RemoveEmptyEntries);//GetStringsFromSingleAOCLine(newLine);

            if (lstLines == null || lstLines.Length == 0)
                return true;

            if (pbParseStatus.Visible)
            {
                int numLines = lstLines.Length;
                pbParseStatus.Value = 0;
                pbParseStatus.Maximum = numLines;
                pbParseStatus.Value = 0;
                SimpleMessage("Parsing " + numLines + " lines...");
                tbMostRecentMessage.Update();
            }

            long lineCount = 0;
            mCurrentSimulatedTime = DateTime.MinValue;
            //now take the lines constructed and parse them
            foreach (string l in lstLines)
            {
                lineCount++;

                curLine = l;
                if (bCancelParse)
                {
                    //clean up
                    //targetHealsToUpload.Clear();
                    //TargetDamagesToUpload.Clear();
                    break;
                }
                
                    Application.DoEvents(); //handle events so we dont choke up

                if (pbParseStatus.Visible)
                {
                    try
                    {
                        pbParseStatus.Value += 1;
                        pbParseStatus.Update();
                    }
                    catch (Exception)
                    {
                        pbParseStatus.Value = 0;
                    }

                }        
                        
        lioVerb = -1; //made class wide variable
        targetFullName = "";
        lioHit = -1;
        lioCrit = -1;
        lioHeal = -1;
        lioSuccessfullyCast = -1;
        lioStartsCasting = -1;
        lioCriticallyHeals = -1;
        lioCriticallySlashes = -1;
        lioCriticallyCrushes = -1;
        lioCriticallyPierces = -1;

        ioHit = -1; ioCrit = -1; ioHeals = -1;
        ioSucessfullyCast = -1; ioCriticallyPierces = -1; ioStartsCasting = -1;
        ioCriticallySlashes = -1; ioCriticallyCrushes = -1; ioCriticallyHeals = -1;
        ioFor = -1; ioEndOfFor = -1;
        ioQuoteInActorName = -1;
                ioDelimAfterAmount = -1;
        ioEndOfFirstWord = -1;

        spellCast = "";

        ioDies = -1;
        actorIsPet = false;

        actorNameHasSpace = false;
        ioCasts = -1;
        lioCasts = -1;

        actorNameHasYour = false;
        hasVerb = false;
        ioVerb = -1;
        actorFullName = null;
        ioStartOfFirstWordOrPlayer = -1;// = curLine.IndexOf("] ") + "] ".Length;

        ioYour = -1;











                //           get time stamp from log
            DateTime now = DateTime.UtcNow; //now is fine
            string sTimeStamp = curLine.Substring(1, 8);
            string sh = sTimeStamp.Substring(0, 2);
            int h = int.Parse(sh);
            string sm = sTimeStamp.Substring(3, 2);
            int m = int.Parse(sm);
            string ss = sTimeStamp.Substring(6, 2);
            int s = int.Parse(ss);
            DateTime oldTimeStamp = timeStamp;
            if (oldTimeStamp.Ticks <= timeStamp.Ticks)
            {
                timeStamp = new DateTime(now.Year, now.Month, now.Day, h, m, s);
            }
            else
            {
                //day changed actually seems likely to happen so lets account for this
                oldTimeStamp = timeStamp;
                timeStamp = oldTimeStamp + new TimeSpan(1, h, m, s);
            }

            if (RealTimeSimulation)
                WaitForTheTime(lineCount);

        /*[00:49:39] Logging finished        
    * [00:19:56] Logging started
                    */
        if (curLine.Contains("Logging finished"))
        {
            this.ParseAccountant.HandleLoggingStarted(timeStamp);

            //CONTINUE THROUGH CURRENT LOOP
            charCount += curLine.Length;
            //GO TO END OF LOOP AND CONTINUE
            continue; //do one small step before moving on
        }
        else if (curLine.Contains("Logging started"))
        {
    
            this.ParseAccountant.HandleLoggingStopped(timeStamp);
            //CONTINUE THROUGH CURRENT LOOP
            charCount += curLine.Length;
            //GO TO END OF LOOP AND CONTINUE
            continue; //do one small step before moving on
        }


        //if (curLine.Contains("critically"))
        //    Debug.Assert(true);//add a breakpoint

        //VERB INDICATORS.. SHOWS INDEX OF PARTICULAR VERB

        ioStartOfFirstWordOrPlayer = curLine.IndexOf("] ") + "] ".Length;

        if (ioStartOfFirstWordOrPlayer != -1) //example of == -1 is [22:02:40] Interrupted!
        {
            int ioSpaceInFirstWord = curLine.IndexOf(" ", ioStartOfFirstWordOrPlayer);
            if (ioSpaceInFirstWord != -1)
                ioEndOfFirstWord = ioSpaceInFirstWord; //CONSIDER ADDING 1 TO THIS AND MAKING SURE THIS DOESNT MESS UP WHERE REFERENCES ARE MADE
        }


        //bool hasVerb = false; //made class wide variable
        //int ioVerb = -1; //made class wide variable

        int lioActorFullName = -1; //ioStartOfFirstWordOrPlayer + actorFullName.Length;

        string theFirstWord_OrPlayer = "";
        //get target if target name is an enemy it might contain spaces
        actorFullName = "";

        int amount = 0;
        bool hasNoTarget = false;

        actorNameHasYour = theFirstWord_OrPlayer.Contains("Your");

        //DO this usual pattern first, of player as actor
        if (ioEndOfFirstWord != -1 && ioStartOfFirstWordOrPlayer != -1)
        {
            ioYour = curLine.IndexOf(your, 0, 18);
            if (ioYour == -1)
            {
                theFirstWord_OrPlayer = curLine.Substring(ioStartOfFirstWordOrPlayer, ioEndOfFirstWord - ioStartOfFirstWordOrPlayer);
                actorFullName = theFirstWord_OrPlayer;
            }
            else
            {
                //YOUR <SPELL> <VERB> <TARGET> FOR <AMOUNT>.
                actorFullName = "You";
                actorNameHasYour = true;
            }
        }


        if (theFirstWord_OrPlayer == "" && ioYour == -1) //example line match: [22:02:40] Interrupted!
        {
            //no noun found
            charCount += curLine.Length;

            //GO TO END OF LOOP AND CONTINUE
            continue; //do one small step before moving on
            //CONTINUE THROUGH CURRENT LOOP
        }


        //START VERB: ONE VERB ONLY, do an order counts evaluation of verbs, once verb in place, skip

        if (!hasVerb)
        {
            ioHit = curLine.IndexOf(hits);//index of word hits
            if (ioHit != -1)
            {
                lioVerb = lioHit = ioHit + hits.Length;
                //ioVerb = lioHit;
                hasVerb = true;    
                actorFullName = curLine.Substring(ioStartOfFirstWordOrPlayer, ioHit - ioStartOfFirstWordOrPlayer);
                ioVerb = ioHit;
            }
        }
        if (!hasVerb)
        {
            ioCrit = curLine.IndexOf(crits);//index of word crits
            if (ioCrit != -1)
            {
                lioVerb = lioCrit = ioCrit + crits.Length;
                //ioVerb = lioCrit;
                hasVerb = true;   
                actorFullName = curLine.Substring(ioStartOfFirstWordOrPlayer, ioCrit - ioStartOfFirstWordOrPlayer);
                ioVerb = ioCrit;
            }
        }
        if (!hasVerb)
        {
            ioSucessfullyCast = curLine.IndexOf(successfullyCast);
            if (ioSucessfullyCast != -1)
            {
                lioVerb = lioSuccessfullyCast = ioSucessfullyCast + successfullyCast.Length;
                //ioVerb = ioSucessfullyCast;
                hasVerb = true;   
                actorFullName = curLine.Substring(ioStartOfFirstWordOrPlayer, ioSucessfullyCast - ioStartOfFirstWordOrPlayer);
                ioVerb = ioSucessfullyCast;

                if (actorFullName != "You")
                    Debug.Assert(true);//but put on breakpoint
            }
        }
        if (!hasVerb)
        {
            ioCriticallySlashes = curLine.IndexOf(criticallySlashes);
            if (ioCriticallySlashes != -1)
            {
                lioVerb = lioCriticallySlashes = ioCriticallySlashes + criticallySlashes.Length;
                //ioVerb = lioCriticallySlashes;
                hasVerb = true;                        
                actorFullName = curLine.Substring(ioStartOfFirstWordOrPlayer, ioCriticallySlashes - ioStartOfFirstWordOrPlayer);
                ioVerb = ioCriticallySlashes;
            }
        }
        if (!hasVerb)
        {
            ioCriticallyCrushes = curLine.IndexOf(criticallyCrushes);
            if (ioCriticallyCrushes != -1)
            {
                lioVerb = lioCriticallyCrushes = ioCriticallyCrushes + criticallyCrushes.Length;
                //ioVerb = lioCriticallyCrushes;
                hasVerb = true;
                actorFullName = curLine.Substring(ioStartOfFirstWordOrPlayer, ioCriticallyCrushes - ioStartOfFirstWordOrPlayer);
                ioVerb = ioCriticallyCrushes;

            }
        }

        if (!hasVerb)
        {
            ioCriticallyHeals = curLine.IndexOf(criticallyHeals);
            if (ioCriticallyHeals != -1)
            {
                //example line: [18:30:40] Malibubarbie critically heals Saangreal's Life-stealer for 108.
                lioVerb = lioCriticallyHeals = ioCriticallyHeals + criticallyHeals.Length;
                //ioVerb = lioCriticallyHeals;
                hasVerb = true;     
                actorFullName = curLine.Substring(ioStartOfFirstWordOrPlayer, ioCriticallyHeals - ioStartOfFirstWordOrPlayer);
                ioVerb = ioCriticallyHeals;
            }
        }
        if (!hasVerb)
        {
            ioCriticallyPierces = curLine.IndexOf(criticallyPierces);
            if (ioCriticallyPierces != -1)
            {
                lioVerb = lioCriticallyPierces = ioCriticallyPierces + criticallyPierces.Length;
                //ioVerb = lioCriticallyHeals;
                ioVerb = ioCriticallyPierces; 
                actorFullName = curLine.Substring(ioStartOfFirstWordOrPlayer, ioVerb - ioStartOfFirstWordOrPlayer);
                hasVerb = true;
            }
        }

        if (!hasVerb)
        {
            ioCasts = curLine.IndexOf(casts);
            if (ioCasts != -1)
            {
                lioVerb = ioCasts + casts.Length;
                //ioVerb = lioCriticallyHeals;
                ioVerb = ioCasts;
                actorFullName = curLine.Substring(ioStartOfFirstWordOrPlayer, ioVerb - ioStartOfFirstWordOrPlayer);
                lioActorFullName = ioVerb + actorFullName.Length;
                hasVerb = true;
            }
        }
        if (!hasVerb)
        {
            ioStartsCasting = curLine.IndexOf(startsCasting);
            if (ioStartsCasting != -1)
            {
                lioVerb = ioStartsCasting + startsCasting.Length;
                //ioVerb = lioCriticallyHeals;
                ioVerb = ioStartsCasting;
                actorFullName = curLine.Substring(ioStartOfFirstWordOrPlayer, ioVerb - ioStartOfFirstWordOrPlayer);
                lioActorFullName = ioVerb;

                //lioActorFullName = ioVerb + actorFullName.Length;
                hasVerb = true;
            }
        }


        if (!hasVerb)
        {
            ioDies = curLine.IndexOf(dies);
            if (ioDies != -1)
            {
                lioVerb = ioDies + dies.Length;
                ioVerb = ioDies;
                actorFullName = curLine.Substring(ioStartOfFirstWordOrPlayer, ioVerb - ioStartOfFirstWordOrPlayer);
                hasNoTarget = true;
                hasVerb = true;

                ParseAccountant.HandleDeath(timeStamp, actorFullName);

                ////
                //CONTINUE THROUGH CURRENT LOOP
                charCount += curLine.Length;
                //GO TO END OF LOOP AND CONTINUE
                continue; //do one small step before moving on
                ////
                ////
            }
        } 
        if (!hasVerb)
        {
            ioHeals = curLine.IndexOf(heals);
            if (ioHeals != -1)
            {
                lioVerb = lioHeal = ioHeals + heals.Length;
                //ioVerb = lioHeal;
                hasVerb = true;
                //example line: [18:30:36] Cookuu's Skirmish Banner's Skirmish heals Aassman for 32.
                actorFullName = curLine.Substring(ioStartOfFirstWordOrPlayer, ioHeals - ioStartOfFirstWordOrPlayer);
                ioVerb = ioHeals;
            }

        }
        actorNameHasSpace = actorFullName.Contains(" ");
        ioQuoteInActorName = actorFullName.IndexOf("'");

        if (hasVerb && !hasNoTarget)
        {
            ///FOR FOR FOR FOR FOR FOR
            ioFor = curLine.LastIndexOf(sFor); //index of word for
            ///FOR FOR FOR FOR FOR FOR
            ///FOR FOR FOR FOR FOR FOR
            ///
            if (ioFor != -1)//&& (ioCrit != -1 || ioHit != -1))
            {                  
                //string testsDmg = curLine.Substring(ioEndOfFor, ioDelimAfterAmount - ioEndOfFor);
                //int testdmg = int.Parse(testsDmg);

                //comes after word for
                //lioFor = ioFor + sFor.Length + 1; //last index of word "for "


                //HAS WORD FOR FOR FOR FOR FOR
                //HAS WORD FOR FOR FOR FOR FOR
                //ACTOR <VERB> <TAR> FOR <AMOUNT>
                if (ioVerb > ioFor) //we were probably mistaken since the spell has the word for in it.
                {
                    ioFor = curLine.LastIndexOf(sFor);
                    ioEndOfFor = ioFor + sFor.Length;
                    int tarLength = ioFor - lioVerb; //some spells have for in them 

                    targetFullName = curLine.Substring(lioVerb, tarLength);
                }
                else if (actorFullName == null && actorFullName.Length == 0 && ioVerb == -1)
                    targetFullName = "";
                else
                {
                    //now that we got the  right for get some other properties based on that index
                    ioEndOfFor = ioFor + sFor.Length;
                    int tarLength = ioFor - lioVerb;
                    targetFullName = curLine.Substring(lioVerb, tarLength);
                }

                    int digitCount = 0;
                    string ch = curLine.Substring(ioEndOfFor, curLine.Length - ioEndOfFor); //should display first few digits of the n

                    ioDelimAfterAmount = curLine.IndexOfAny(new char[] {' ', '.'}, ioEndOfFor);
                    if (ioDelimAfterAmount == -1)
                    {
                        Debug.WriteLine("Skipping line: " + curLine);
                        charCount += curLine.Length;
                        continue;
                    }
                    digitCount = ioDelimAfterAmount - ioEndOfFor;
                string samount = curLine.Substring(ioEndOfFor, digitCount);
                try
                {
                    amount = int.Parse(samount);
                }
                catch (Exception parseException)
                {
                    //"[17:55:50] Jaynor casts A Soul for our Feast IV"
                    //get spell
                    if (ioVerb != -1)
                    {

                        int ioActor = curLine.IndexOf(actorFullName);
                        if (ioActor != -1)
                        {
                            spellCast = curLine.Substring(lioVerb, curLine.Length - lioVerb);
                            this.ParseAccountant.HandleCast(timeStamp, (string)actorFullName, (string)spellCast);
                        }
                        else
                            throw parseException;
                    }
                    else throw parseException; //should not throw if in release
                }

            } else
                targetFullName = null;
        } //end of if has verb

        bool isDamage = false, isHeal = false, isCast = false;

        isDamage = targetFullName != null && targetFullName != ""
            && (ioCrit != -1 || ioCriticallyCrushes != -1 || 
            ioCriticallySlashes !=-1 || ioCriticallyPierces !=-1 ||
            ioHit != -1) && ioFor != -1;

        isHeal = (ioHeals != -1 || ioCriticallyHeals != -1)
            && targetFullName != null && targetFullName != ""
            && ioFor != -1;
                    
        isCast = (ioSucessfullyCast != -1 || ioCasts != -1 || ioStartsCasting !=-1);

        //if ((isDamage || isHeal) && tar.Contains("'")) //
        //{
        //    //EXAMPLE MATCH: 		
        //    //curLine=	"[18:48:37] Your Demon Warlord's Infernal Flames hits Tehdarknass's Demon Warlord for 66 fire damage."	string
        //    //do not account for this yet, only could give a running total of health on pet
        //    //healing pets or damaging pets are not considered anything special 
        //    //i dont think they genrerate threat either, and dont count as damage or healing
        //    Debug.WriteLine("SKIPPING" + curLine);
        //    continue;
        //}

        //////GET AN ENEMY NAME IF ANY
        //////see if we can find  an enemy name 
        ////if (isDamage || isHeal && actorFullName != "")
        ////{
        ////    //int ioSpellInFullName = actorFullName.IndexOf("'s Spell"); //Player's spell/renewal etc
        ////    //if (ioSpellInFullName != -1) //remove this word spell --although it denotes spell damage
        ////    //    actorFullName = actorFullName.Replace("'s Spell", "");\

        ////    //bool isPet = false;
        ////    //if (possibleEnemyTarget.Contains("'") && possibleEnemyTarget.Contains()

        ////    //int ioQuoteInFullName = actorFullName.IndexOf("'s");
        ////    //if (ioQuoteInFullName >= 1)
        ////    //    actorFullName = actorFullName.Substring(0, ioQuoteInFullName - 1);

        ////    //if (actorFullName.Contains("Health Potion"))
        ////    //    actorFullName = "Health Potion";

        ////}
                    
        string petNameActor = null;

        if (actorNameHasYour)
        {
            if (hasVerb && lioActorFullName == -1)
                lioActorFullName = ioEndOfFirstWord; //should set above because it is contextually the same

            actorFullName = "You";
        }
        else if (ioQuoteInActorName != -1)
        {
            GetPetsIfAny(ioQuoteInActorName, ref lioActorFullName, ref spellCast);
        } //no quotes in name
        else // if (actorNameHasSpace && !actorNameHasYour && actorFullName != "" && ioQuoteInActorName == -1)
        {
            if (lioActorFullName == -1 && ioVerb != -1)
            {
                lioActorFullName = ioVerb;
            }
        }






        //new
        //SHOULD CHECK FOR TARGET CONTAINING PET AND PARSE ACCORDINGLY
        //end new




        //[00:25:53] Your Spell hits Outer Membrane for 311 electrical damage.
        if (actorFullName.Contains("Spell hits"))
            Debug.Assert(true);

        //should assert there is no spell when ioCriticallyPierces != -1 
        //else if (lioVerb != -1 && (ioCrit != -1 || io))
        //{
        //    //actorFullName = curLine.Substring(ioStartOfFirstWordOrPlayer, ioVerb - ioStartOfFirstWordOrPlayer);
        //    spellCast = curLine.Substring(lioActorFullName, curLine.Length - lioActorFullName);
        //} 

        if (isCast)
        {
            //[23:30:54] You start casting Soothe the Soul
            //[23:30:55] You successfully cast spell Soothe the Soul

            //[23:31:40] You start casting Shroud of the Gods
            //[23:31:41] You successfully cast spell Shroud of the Gods

            //spellCast = curLine.Substring(lioVerb, curLine.Length - lioVerb);
            this.ParseAccountant.HandleCast(timeStamp, (string)actorFullName, (string)spellCast);

            if (ioCasts != -1)
            {
                spellCast = curLine.Substring(lioVerb, curLine.Length - lioVerb);
            }
            else if (ioSucessfullyCast != -1)
            {
                const string wordSpell = "spell ";
                int ioWordSpell = curLine.IndexOf("spell ", lioVerb - 1);
                if (ioWordSpell == -1)
                    spellCast = curLine.Substring(lioVerb, curLine.Length - lioVerb);
                else
                {
                    int lioWordSpell = ioWordSpell + wordSpell.Length;
                    spellCast = curLine.Substring(lioWordSpell, curLine.Length - lioWordSpell);
                }
            }
            else if (ioStartsCasting != -1)
            {
                spellCast = curLine.Substring(lioStartsCasting, curLine.Length - lioStartsCasting);
            }

            //this.threatCalculator.HandleCast((string)actorFullName, (string)spellCast);

        }//end of if (!(isHeal || isDamage))
        //if (isCast && lioVerb != -1)
        //{
        //    spellCast = curLine.Substring(lioVerb, curLine.Length - lioVerb);
        //}
        if (isHeal && lioVerb != -1)
        {            
            if (!actorIsPet)
            {    
                IsTargetPet(ioYour);    
            }
                    
        }
                
        //if (isActorsPet)
        //{
        //    if (isDamage)
        //    {
        //        ParsedLogEvent damageEvent = new ParsedLogEvent(actorFullName, targetFullName, amount, spellCast);
        //        ParseAccountant.HandlePetIsDamagingActor(damageEvent, petNameActor);
        //    }
        //    if (isHeal)
        //    {
        //        ParsedLogEvent healEvent = new ParsedLogEvent(actorFullName, targetFullName, amount, spellCast);
        //        ParseAccountant.HandlePetIsHealingActor(healEvent, petNameActor);
        //    }
                
        //}
        if (targetFullName == "you") //happens
        {
            targetFullName = "You";
        }

        if (isDamage)
        {
            //example common type of matching line "[21:31:39] Your Incinerate (5) hits Bear for 160 fire damage."
            //example unusual line matching "[18:19:49] Belphegor's Sadistic Soul hits Yaremka the Soul Eater for 5 unholy damage."
            //common match : 		curLine	"[18:48:37] Your Demon Warlord's Infernal Flames hits Tehdarknass's Demon Warlord for 66 fire damage."	string
            //curLine	"[19:22:11] Plains Cobra's Venom Strike hits you for 1 poison damage."	string

            //unusual line: "[18:20:48] Belphegor's Blood for Aid IV critically slashes Yaremka the Soul Eater for 310."
            //not the spell has the word for in it

            ParsedLogEvent damageEvent = new ParsedLogEvent(actorFullName, actorIsPet, targetFullName, amount, spellCast, timeStamp);

            ParseAccountant.HandleDamage(damageEvent);

            if (theFirstWord_OrPlayer == "You")
            {
                this.currentTarget = targetFullName;
            }

        }                   //EXAMPLE: [16:35:34] Unholymode's Siphon Unlife hits Unholymode's Blighted One for 90 damage.
        //if (tar == "Unholymode's Blighted One" && thePlayer == "Unholymode's")
        else if (isHeal)
        {
            //PUT AFTER PARSING
            ParsedLogEvent healEvent = new ParsedLogEvent(actorFullName, actorIsPet, targetFullName, amount, spellCast, timeStamp);
            if (actorFullName != null && actorFullName != "You")
                Debug.Assert(true);
            ParseAccountant.HandleHeal(healEvent);
        }

  

        //begin: update the size of the file
        charCount += curLine.Length;
        //end: update the size of the file
    } //end of for

    if (charCount > fileLength)
    {
        Debug.Assert(true);//assert positive but with breakpoint!
        charCount = fileLength; // this shouldnt happen but it does
    }
    if (pbParseStatus.Visible)
    {
        int numLines = lstLines.Length;
        pbParseStatus.Value = 0;
        SimpleMessage("Done loading " + numLines + " lines...");
        tbMostRecentMessage.Update();
    }

    if (this.updateTime !=null)
        this.updateTime.Stop();
    this.updateTime = null;
    this.mCurrentSimulatedTime = DateTime.MinValue;

    return bCancelParse;
}//end of procedure

        private void WaitForTheTime(long lineCount)
        {
            if (RealTimeSimulation && updateTime == null || updateTime.Enabled == false)//currentSimulatedTime == DateTime.MinValue)
            {
                DateTime now = DateTime.UtcNow;
                currentSimulatedTime = new DateTime(now.Year, now.Month, now.Day, timeStamp.Hour, timeStamp.Minute, timeStamp.Second, timeStamp.Millisecond); //initialize
                updateTime = new System.Timers.Timer();
                updateTime.Interval = 1000;
                updateTime.Elapsed += new System.Timers.ElapsedEventHandler(updateTime_Elapsed);
                updateTime.Start();
                realTimeSimulationPaused = false;
            }

            double TimeToWaitms; 
            TimeToWaitms = (timeStamp - currentSimulatedTime).TotalMilliseconds;
            while ((RealTimeSimulation && RealTimeSimulationPaused) || 
                (RealTimeSimulation &&
                TimeToWaitms > 0  &&
                !CancelParse))
            {
                Application.DoEvents();

                if (RealTimeSimulationPaused)
                {

                }
                else
                {
                    //System.Threading.Thread.CurrentThread.Join(durationToWait);

                    //SimpleMessage(lineCount + "/" + lstLines.Length);
                    TimeToWaitms = (timeStamp - currentSimulatedTime).TotalMilliseconds / this.simulationFrequency;
                    if (TimeToWaitms >= 1000)
                        SimpleMessage("Waiting for " + (long)TimeToWaitms + "ms. Press N for next. M for next minute. P to Pause/Play.");
                    if (TimeToWaitms <= 0)
                    {
                        SimpleMessage("Parsing next message...");
                        return; //need to play catch up so get going
                    }
                }
            }
   


            //if (RealTimeSimulation && mDisplayUpdate != null)
            //    mDisplayUpdate();
        }

        void updateTime_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        {
            if (!realTimeSimulationPaused)
            {
                int elapsedMS = (int)(updateTime.Interval * this.simulationFrequency);
                currentSimulatedTime += new TimeSpan(0, 0, 0, 0, elapsedMS);
            }
        }

    private void IsTargetPet(int ioYour)
    {

        if (ioQuoteInActorName != -1)
        {
            //find last index of actor
            int ioQuoteAfterName = curLine.IndexOf("'s");
            if (ioQuoteAfterName == -1)
                ioQuoteAfterName = curLine.IndexOf("'");
            else //if (ioQuoteAfterName != -1)
            {
                //spellCast = curLine.Substring(ioQuoteAfterName + 1, curLine.Length - ioVerb);
            }
        }
        else
        {
            if (ioYour != -1)
            {
                spellCast = curLine.Substring(ioEndOfFirstWord + 1, ioVerb - ioEndOfFirstWord - 1);
            }
            else
            {
                //NOUN'S SPELL VERB TARGET
                int ioQuote = curLine.LastIndexOf("'s", ioVerb, ioVerb);
                if (ioQuote == -1)
                {
                    ioQuote = curLine.LastIndexOf("'", ioVerb, ioVerb);
                    ioQuote += 2;
                }
                else
                {
                    ioQuote += 3;
                }
                if (ioQuote != -1)
                {

                    spellCast = curLine.Substring(ioQuote, ioVerb - ioQuote);
                }
            }
        }
    }



        //remove params: int ioVerb, string targetFullName, ref string actorFullName,
        private string GetPetsIfAny(int ioQuoteInActorName, ref int lioActorFullName, ref string spellCast )
        {
            string petNameActor = null;
            //could be a pet of the boss who has an entirely new aggro list
            //could make a list of all known pet names, but i will go the route of if the player has
            // does some action to his pet then we will know it if it contains his name...
            if (actorFullName == null)
                return null;

            if (targetFullName != null && targetFullName.Contains(actorFullName))
            {
                //Possible line: Person's pet (or potio0n) hits/crits/heals...This add to persons own damage or healing
                //Possible line: Balbet-Zhun's Trial of the Godslayer heals Balbet-Zhun for 126544.
                petNameActor = actorFullName;

                actorFullName = actorFullName.Substring(0, ioQuoteInActorName); //do this after saving away the potion or pet actor on the player

                //DENY THIS MESSAGE FOR NOW, NOT ADDING PET EVENTS TO THE PET OWNER
                //DENY THIS MESSAGE FOR NOW, NOT ADDING PET EVENTS TO THE PET OWNER
                actorIsPet = true;

                if (hasVerb && lioActorFullName == -1)
                    lioActorFullName = ioVerb + actorFullName.Length;

            }
            else
            {
                //ex: Shaqfu's <Corruptor's Bone Horde> hits

                //allow the target, probably a pet of some other mob or boss
                //theFirstWord_OrPlayer = actorFullName;
                petNameActor = actorFullName;

                if (actorFullName.Contains("'s"))
                    actorFullName = actorFullName.Substring(0, ioQuoteInActorName);
                else
                    actorFullName = actorFullName.Substring(0, ioQuoteInActorName - 1); //carlins' blank hits for blank, etc. Be sure to get rid of s via the -1
                actorIsPet = true;

                //get spell
                if (ioVerb != -1)
                {

                    int ioActor = curLine.IndexOf(actorFullName);
                    if (ioActor != -1)
                    {
                        int lioActor = ioActor + actorFullName.Length;
                        int ioStartOfSpell = curLine.IndexOf(" ", lioActor);
                        spellCast = curLine.Substring(ioStartOfSpell + 1, ioVerb - ioStartOfSpell);

                        lioActorFullName = lioActor;

                    }
                    else if (lioActorFullName == -1)
                        lioActorFullName = actorFullName.Length + ioVerb;

                }
            }
            return petNameActor;
        }

        private DateTime mCurrentSimulatedTime;
        public DateTime currentSimulatedTime { 
            get { return mCurrentSimulatedTime;}
            set { mCurrentSimulatedTime = value; }
        }

        public void GotoNextEvent()
        {
            SimpleMessage("Skipped ahead next event at " + mCurrentSimulatedTime.ToShortTimeString());
            mCurrentSimulatedTime = timeStamp; //goto next message (see parse... method and definately see WaitForTheTime)
        }

        public void SkipAheadHour()
        {
            mCurrentSimulatedTime += new TimeSpan(1, 0, 0);
            SimpleMessage("Skipped ahead one hour to " + mCurrentSimulatedTime.ToShortTimeString());
        }
        public void SkipAheadMinute()
        {
            mCurrentSimulatedTime += new TimeSpan(0, 1, 0);
            SimpleMessage("Skipped ahead one minute to " + mCurrentSimulatedTime.ToShortTimeString());
        }
        public void SkipAheadSeconds(int howMany)
        {
            mCurrentSimulatedTime += new TimeSpan(0, 0, howMany);
            SimpleMessage("Skipped ahead " + howMany +  "s" + mCurrentSimulatedTime.ToShortTimeString());        }

        private bool realTimeSimulationPaused = false;

        public bool RealTimeSimulationPaused
        {
            get { return realTimeSimulationPaused; }
            set { realTimeSimulationPaused = value;
                if (realTimeSimulationPaused && RealTimeSimulation)
                {
                    SimpleMessage("Simulation paused. Press P to toggle pausing.");
                    this.updateTime.Enabled = false;
                    
                }
                else if (RealTimeSimulation && realTimeSimulationPaused == false)
                {
                    SimpleMessage("Simulation resumed. Press P to pause.");
                    this.updateTime.Enabled = true;
                }
            }
        }


    } //end of clavss
} //end of namespace